/**
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "js_bigint.h"
#include <cstdint>
#include "plugins/ecmascript/runtime/js_array.h"
#include "plugins/ecmascript/runtime/object_factory.h"

namespace ark::ecmascript {
class ObjectFactory;
constexpr char DP[] = "0123456789abcdefghijklmnopqrstuvwxyz";  // NOLINT(modernize-avoid-c-arrays)
static int CharToInt(char c)
{
    int res = 0;
    if (c >= '0' && c <= '9') {
        res = c - '0';
    } else if (c >= 'A' && c <= 'Z') {
        res = c - 'A' + 10;  // NOLINT(readability-magic-numbers) 10:res must Greater than 10.
    } else if (c >= 'a' && c <= 'z') {
        res = c - 'a' + 10;  // NOLINT(readability-magic-numbers) 10:res must Greater than 10
    }
    return res;
}

static std::string Division(std::string &num, uint32_t conversionToRadix, uint32_t currentRadix, uint32_t &remain)
{
    ASSERT(conversionToRadix != 0);
    uint32_t temp = 0;
    remain = 0;
    for (char &c : num) {
        temp = (currentRadix * remain + static_cast<uint32_t>(CharToInt(c)));
        c = DP[temp / conversionToRadix];
        remain = temp % conversionToRadix;
    }
    int count = 0;
    while (num[count] == '0') {
        count++;
    }
    return num.substr(count);
}

std::string BigIntHelper::Conversion(const std::string &num, uint32_t conversionToRadix, uint32_t currentRadix)
{
    ASSERT(conversionToRadix != 0);
    std::string newNum = num;
    std::string res;
    uint32_t remain = 0;
    while (!newNum.empty()) {
        newNum = Division(newNum, conversionToRadix, currentRadix, remain);
        res.insert(res.begin(), DP[remain]);
    }
    return res;
}

JSHandle<BigInt> BigIntHelper::SetBigInt(JSThread *thread, const std::string &numStr, uint32_t currentRadix)
{
    int flag = 0;
    if (numStr[0] == '-') {
        flag = 1;
    }

    std::string binaryStr;
    if (currentRadix != BigInt::BINARY) {
        binaryStr = Conversion(numStr.substr(flag), BigInt::BINARY, currentRadix);
    } else {
        binaryStr = numStr.substr(flag);
    }

    JSHandle<BigInt> bigInt;
    size_t binaryStrLen = binaryStr.size();
    size_t len = binaryStrLen / BigInt::DATEBITS;
    size_t mod = binaryStrLen % BigInt::DATEBITS;
    int index = 0;
    if (mod == 0) {
        index = static_cast<int>(len - 1);
        bigInt = BigInt::CreateBigInt(thread, len);
    } else {
        len++;
        index = static_cast<int>(len - 1);
        bigInt = BigInt::CreateBigInt(thread, len);
        uint32_t val = 0;
        for (size_t i = 0; i < mod; ++i) {
            val <<= 1U;
            val |= static_cast<uint32_t>(binaryStr[i] - '0');
        }
        BigInt::SetDigit(thread, bigInt, index, val);
        index--;
    }
    if (flag == 1) {
        bigInt->SetSign(true);
    }
    size_t i = mod;
    while (i < binaryStrLen) {
        uint32_t val = 0;
        for (size_t j = 0; j < BigInt::DATEBITS && i < binaryStrLen; ++j, ++i) {
            val <<= 1U;
            val |= static_cast<uint32_t>(binaryStr[i] - '0');
        }
        BigInt::SetDigit(thread, bigInt, index, val);
        index--;
    }
    return BigIntHelper::RightTruncate(thread, bigInt);
}

JSHandle<BigInt> BigIntHelper::RightTruncate(JSThread *thread, JSHandle<BigInt> x)
{
    int len = static_cast<int>(x->GetLength());
    ASSERT(len != 0);
    if (len == 1 && x->GetDigit(0) == 0) {
        x->SetSign(false);
        return x;
    }
    int index = len - 1;
    if (x->GetDigit(index) != 0) {
        return x;
    }
    while (index >= 0) {
        if (x->GetDigit(index) != 0) {
            break;
        }
        index--;
    }
    JSHandle<TaggedArray> array(thread, x->GetData());
    if (index == -1) {
        array->Trim(thread, 1);
    } else {
        array->Trim(thread, index + 1);
    }
    if (x->IsZero()) {
        x->SetSign(false);
    }
    return x;
}

std::string BigIntHelper::GetBinary(const JSHandle<BigInt> &bigInt)
{
    int index = 0;
    auto len = static_cast<int>(bigInt->GetLength());
    int strLen = BigInt::DATEBITS * len;
    std::string res(strLen, '0');
    int strIndex = strLen - 1;
    while (index < len) {
        int bityLen = BigInt::DATEBITS;
        uint32_t val = bigInt->GetDigit(index);
        while (bityLen-- != 0) {
            res[strIndex--] = (val & 1U) + '0';
            val = val >> 1UL;
        }
        index++;
    }
    size_t count = 0;
    size_t resLen = res.size();
    for (size_t i = 0; i < resLen; ++i) {
        if (res[i] != '0') {
            break;
        }
        count++;
    }
    if (count == resLen) {
        return "0";
    }
    return res.substr(count);
}

uint32_t BigInt::GetDigit(uint32_t index) const
{
    TaggedArray *taggedArray = TaggedArray::Cast(GetData().GetTaggedObject());
    JSTaggedValue digit = taggedArray->Get(index);
    return static_cast<uint32_t>(digit.GetInt());
}

void BigInt::SetDigit(JSThread *thread, JSHandle<BigInt> bigInt, uint32_t index, uint32_t digit)
{
    JSHandle<TaggedArray> digitsHandle(thread, bigInt->GetData().GetTaggedObject());
    digitsHandle->Set(thread, index, JSTaggedValue(static_cast<int32_t>(digit)));
}

uint32_t BigInt::GetLength() const
{
    TaggedArray *taggedArray = TaggedArray::Cast(GetData().GetTaggedObject());
    return taggedArray->GetLength();
}

JSHandle<BigInt> BigInt::CreateBigInt(JSThread *thread, uint32_t size)
{
    ASSERT(size < MAXSIZE);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<BigInt> bigInt = factory->NewBigInt();
    JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(size);
    bigInt->SetData(thread, taggedArray.GetTaggedValue());
    return bigInt;
}

// 6.1.6.2.13
bool BigInt::Equal(JSThread *thread, const JSTaggedValue &x, const JSTaggedValue &y)
{
    [[maybe_unused]] EcmaHandleScope scope(thread);
    JSHandle<BigInt> xHandle(thread, x);
    JSHandle<BigInt> yHandle(thread, y);
    return Equal(xHandle, yHandle);
}

bool BigInt::Equal(const JSHandle<BigInt> &x, const JSHandle<BigInt> &y)
{
    if (x->GetSign() != y->GetSign() || x->GetLength() != y->GetLength()) {
        return false;
    }
    for (uint32_t i = 0; i < x->GetLength(); ++i) {
        if (x->GetDigit(i) != y->GetDigit(i)) {
            return false;
        }
    }
    return true;
}

// 6.1.6.2.14
bool BigInt::SameValue(JSThread *thread, const JSTaggedValue &x, const JSTaggedValue &y)
{
    return Equal(thread, x, y);
}

// 6.1.6.2.15
bool BigInt::SameValueZero(JSThread *thread, const JSTaggedValue &x, const JSTaggedValue &y)
{
    return Equal(thread, x, y);
}

void BigInt::InitializationZero(JSThread *thread, JSHandle<BigInt> bigInt)
{
    uint32_t len = bigInt->GetLength();
    for (uint32_t i = 0; i < len; ++i) {
        SetDigit(thread, bigInt, i, 0);
    }
}

JSHandle<BigInt> BigInt::BitwiseOp(JSThread *thread, Operate op, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    uint32_t maxLen = 0;
    uint32_t minLen = 0;
    uint32_t xlen = x->GetLength();
    uint32_t ylen = y->GetLength();
    if (xlen > ylen) {
        maxLen = xlen;
        minLen = ylen;
    } else {
        maxLen = ylen;
        minLen = xlen;
    }
    JSHandle<BigInt> bigInt = BigInt::CreateBigInt(thread, maxLen);
    InitializationZero(thread, bigInt);
    for (size_t i = 0; i < minLen; ++i) {
        if (op == Operate::OR) {
            SetDigit(thread, bigInt, i, x->GetDigit(i) | y->GetDigit(i));
        } else if (op == Operate::AND) {
            SetDigit(thread, bigInt, i, x->GetDigit(i) & y->GetDigit(i));
        } else {
            ASSERT(op == Operate::XOR);
            SetDigit(thread, bigInt, i, x->GetDigit(i) ^ y->GetDigit(i));
        }
    }
    if (op == Operate::OR || op == Operate::XOR) {
        if (xlen > ylen) {
            for (size_t i = ylen; i < xlen; ++i) {
                SetDigit(thread, bigInt, i, x->GetDigit(i));
            }
        } else if (ylen > xlen) {
            for (size_t i = xlen; i < ylen; ++i) {
                SetDigit(thread, bigInt, i, y->GetDigit(i));
            }
        }
    }
    return BigIntHelper::RightTruncate(thread, bigInt);
}

JSHandle<BigInt> OneIsNegativeAND(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    JSHandle<BigInt> yVal = BigInt::BitwiseSubOne(thread, y, y->GetLength());
    uint32_t xLength = x->GetLength();
    uint32_t yLength = yVal->GetLength();
    uint32_t minLen = xLength;
    if (xLength > yLength) {
        minLen = yLength;
    }
    JSHandle<BigInt> newBigInt = BigInt::CreateBigInt(thread, xLength);
    uint32_t i = 0;
    while (i < minLen) {
        uint32_t res = x->GetDigit(i) & ~(yVal->GetDigit(i));
        BigInt::SetDigit(thread, newBigInt, i, res);
        ++i;
    }
    while (i < xLength) {
        BigInt::SetDigit(thread, newBigInt, i, x->GetDigit(i));
        ++i;
    }
    return BigIntHelper::RightTruncate(thread, newBigInt);
}

// 6.1.6.2.20 BigInt::bitwiseAND ( x, y )
JSHandle<BigInt> BigInt::BitwiseAND(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    if (x->GetSign() && y->GetSign()) {
        // (-x) & (-y) == -(((x-1) | (y-1)) + 1)
        JSHandle<BigInt> xVal = BitwiseSubOne(thread, x, x->GetLength());
        JSHandle<BigInt> yVal = BitwiseSubOne(thread, y, y->GetLength());
        JSHandle<BigInt> temp = BitwiseOp(thread, Operate::OR, xVal, yVal);
        JSHandle<BigInt> res = BitwiseAddOne(thread, temp);
        return res;
    }
    if (x->GetSign() != y->GetSign()) {
        // x & (-y) == x & ~(y-1)
        if (!x->GetSign()) {
            return OneIsNegativeAND(thread, x, y);
        }
        return OneIsNegativeAND(thread, y, x);
    }
    return BitwiseOp(thread, Operate::AND, x, y);
}

JSHandle<BigInt> OneIsNegativeXOR(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    JSHandle<BigInt> yVal = BigInt::BitwiseSubOne(thread, y, y->GetLength());
    JSHandle<BigInt> temp = BigInt::BitwiseOp(thread, Operate::XOR, x, yVal);
    JSHandle<BigInt> res = BigInt::BitwiseAddOne(thread, temp);
    return res;
}

// 6.1.6.2.21 BigInt::bitwiseOR ( x, y )
JSHandle<BigInt> BigInt::BitwiseXOR(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    if (x->GetSign() && y->GetSign()) {
        // (-x) ^ (-y) == (x-1) ^ (y-1)
        JSHandle<BigInt> xVal = BitwiseSubOne(thread, x, x->GetLength());
        JSHandle<BigInt> yVal = BitwiseSubOne(thread, y, y->GetLength());
        return BitwiseOp(thread, Operate::XOR, xVal, yVal);
    }
    if (x->GetSign() != y->GetSign()) {
        // x ^ (-y) == -((x ^ (y-1)) + 1)
        if (!x->GetSign()) {
            return OneIsNegativeXOR(thread, x, y);
        }
        return OneIsNegativeXOR(thread, y, x);
    }
    return BitwiseOp(thread, Operate::XOR, x, y);
}

JSHandle<BigInt> BigInt::BitwiseSubOne(JSThread *thread, JSHandle<BigInt> bigInt, uint32_t maxLen)
{
    ASSERT(!bigInt->IsZero());
    ASSERT(maxLen >= bigInt->GetLength());

    JSHandle<BigInt> newBigInt = BigInt::CreateBigInt(thread, maxLen);

    uint32_t bigIntLen = bigInt->GetLength();
    uint32_t carry = 1;
    for (uint32_t i = 0; i < bigIntLen; i++) {
        uint32_t bigIntCarry = 0;
        BigInt::SetDigit(thread, newBigInt, i, BigIntHelper::SubHelper(bigInt->GetDigit(i), carry, bigIntCarry));
        carry = bigIntCarry;
    }
    ASSERT(!carry);
    for (uint32_t i = bigIntLen; i < maxLen; i++) {
        BigInt::SetDigit(thread, newBigInt, i, carry);
    }
    return BigIntHelper::RightTruncate(thread, newBigInt);
}

JSHandle<BigInt> BigInt::BitwiseAddOne(JSThread *thread, JSHandle<BigInt> bigInt)
{
    uint32_t bigIntLength = bigInt->GetLength();

    bool needExpend = true;
    for (uint32_t i = 0; i < bigIntLength; i++) {
        if (std::numeric_limits<uint32_t>::max() != bigInt->GetDigit(i)) {
            needExpend = false;
            break;
        }
    }
    uint32_t newLength = bigIntLength;
    if (needExpend) {
        newLength += 1;
    }
    JSHandle<BigInt> newBigInt = BigInt::CreateBigInt(thread, newLength);

    uint32_t carry = 1;
    for (uint32_t i = 0; i < bigIntLength; i++) {
        uint32_t bigIntCarry = 0;
        BigInt::SetDigit(thread, newBigInt, i, BigIntHelper::AddHelper(bigInt->GetDigit(i), carry, bigIntCarry));
        carry = bigIntCarry;
    }
    if (needExpend) {
        BigInt::SetDigit(thread, newBigInt, bigIntLength, carry);
    } else {
        ASSERT(!carry);
    }
    newBigInt->SetSign(true);
    return BigIntHelper::RightTruncate(thread, newBigInt);
}

JSHandle<BigInt> OneIsNegativeOR(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    uint32_t xLength = x->GetLength();
    uint32_t maxLen = xLength;
    if (maxLen < y->GetLength()) {
        maxLen = y->GetLength();
    }
    JSHandle<BigInt> yVal = BigInt::BitwiseSubOne(thread, y, maxLen);
    uint32_t yLength = yVal->GetLength();
    uint32_t minLen = xLength;
    if (minLen > yLength) {
        minLen = yLength;
    }
    JSHandle<BigInt> newBigInt = BigInt::CreateBigInt(thread, yLength);
    uint32_t i = 0;
    while (i < minLen) {
        uint32_t res = ~(x->GetDigit(i)) & yVal->GetDigit(i);
        BigInt::SetDigit(thread, newBigInt, i, res);
        ++i;
    }
    while (i < yLength) {
        BigInt::SetDigit(thread, newBigInt, i, yVal->GetDigit(i));
        ++i;
    }
    JSHandle<BigInt> temp = BigIntHelper::RightTruncate(thread, newBigInt);
    JSHandle<BigInt> res = BigInt::BitwiseAddOne(thread, temp);
    res->SetSign(true);
    return res;
}

// 6.1.6.2.22 BigInt::bitwiseOR ( x, y )
JSHandle<BigInt> BigInt::BitwiseOR(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    if (x->GetSign() && y->GetSign()) {
        // (-x) | (-y) == -(((x-1) & (y-1)) + 1)
        uint32_t maxLen = x->GetLength();
        uint32_t yLen = y->GetLength();
        maxLen < yLen ? maxLen = yLen : 0;
        JSHandle<BigInt> xVal = BitwiseSubOne(thread, x, maxLen);
        JSHandle<BigInt> yVal = BitwiseSubOne(thread, y, yLen);
        JSHandle<BigInt> temp = BitwiseOp(thread, Operate::AND, xVal, yVal);
        JSHandle<BigInt> res = BitwiseAddOne(thread, temp);
        res->SetSign(true);
        return res;
    }
    if (x->GetSign() != y->GetSign()) {
        // x | (-y) == -(((y-1) & ~x) + 1)
        if (!x->GetSign()) {
            return OneIsNegativeOR(thread, x, y);
        }
        return OneIsNegativeOR(thread, y, x);
    }
    return BitwiseOp(thread, Operate::OR, x, y);
}

// 6.1.6.2.23 BigInt::toString ( x )
JSHandle<EcmaString> BigInt::ToString(JSThread *thread, JSHandle<BigInt> bigInt, uint32_t conversionToRadix)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    std::string result = bigInt->ToStdString(thread, conversionToRadix);
    return factory->NewFromStdString(result);
}

std::string BigInt::ToStdString(JSThread *thread, uint32_t conversionToRadix) const
{
    JSHandle<BigInt> thisHandle(thread, JSTaggedValue(this));
    std::string result = BigIntHelper::Conversion(BigIntHelper::GetBinary(thisHandle), conversionToRadix, BINARY);
    if (GetSign()) {
        result = "-" + result;
    }
    return result;
}

JSTaggedValue BigInt::NumberToBigInt(JSThread *thread, JSHandle<JSTaggedValue> number)
{
    if (!number->IsInteger()) {
        THROW_RANGE_ERROR_AND_RETURN(thread, "The number cannot be converted to a BigInt because it is not an integer",
                                     JSTaggedValue::Exception());
    }
    double num = number->GetNumber();
    if (num == 0.0) {
        return Int32ToBigInt(thread, 0).GetTaggedValue();
    }

    // Bit operations must be of integer type
    uint64_t bits = 0;
    if (memcpy_s(&bits, sizeof(bits), &num, sizeof(num)) != EOK) {
        LOG_ECMA(FATAL) << "memcpy_s failed";
        UNREACHABLE();
    }
    // Take out bits 62-52 (11 bits in total) and subtract 1023
    // NOLINTNEXTLINE(readability-magic-numbers)
    uint64_t integerDigits = ((bits >> base::DOUBLE_SIGNIFICAND_SIZE) & 0x7FFU) - base::DOUBLE_EXPONENT_BIAS;
    uint32_t mayNeedLen = integerDigits / BigInt::DATEBITS + 1;

    JSHandle<BigInt> bigInt = CreateBigInt(thread, mayNeedLen);
    bigInt->SetSign(num < 0);
    uint64_t mantissa = (bits & base::DOUBLE_SIGNIFICAND_MASK) | base::DOUBLE_HIDDEN_BIT;
    int mantissaSize = base::DOUBLE_SIGNIFICAND_SIZE;

    uint32_t leftover = 0;
    bool isFirstInto = true;
    for (int index = static_cast<int>(mayNeedLen - 1); index >= 0; --index) {
        uint32_t doubleNum = 0;
        if (isFirstInto) {
            isFirstInto = false;
            leftover = mantissaSize - static_cast<int>(integerDigits % BigInt::DATEBITS);
            doubleNum = static_cast<uint32_t>(mantissa >> leftover);
            mantissa = mantissa << (64U - leftover);  // NOLINT(readability-magic-numbers) 64 : double bits size
            BigInt::SetDigit(thread, bigInt, index, doubleNum);
        } else {
            leftover -= BigInt::DATEBITS;
            doubleNum = static_cast<uint32_t>(mantissa >> BigInt::DATEBITS);
            mantissa = mantissa << BigInt::DATEBITS;
            BigInt::SetDigit(thread, bigInt, index, doubleNum);
        }
    }
    return BigIntHelper::RightTruncate(thread, bigInt).GetTaggedValue();
}

JSHandle<BigInt> BigInt::Int32ToBigInt(JSThread *thread, const int &number)
{
    JSHandle<BigInt> bigInt = CreateBigInt(thread, 1);
    uint32_t value = 0;
    bool sign = number < 0;
    if (sign) {
        value = static_cast<uint32_t>(-(number + 1)) + 1;
    } else {
        value = number;
    }
    BigInt::SetDigit(thread, bigInt, 0, value);
    bigInt->SetSign(sign);
    return bigInt;
}

JSHandle<BigInt> BigInt::Int64ToBigInt(JSThread *thread, const int64_t &number)
{
    JSHandle<BigInt> bigInt = CreateBigInt(thread, 2);  // 2 : one int64_t bits need two uint32_t bits
    uint64_t value = 0;
    bool sign = number < 0;
    if (sign) {
        value = static_cast<uint64_t>(-(number + 1)) + 1;
    } else {
        value = number;
    }
    auto *addr = reinterpret_cast<uint32_t *>(&value);
    BigInt::SetDigit(thread, bigInt, 0, *(addr));
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
    BigInt::SetDigit(thread, bigInt, 1, *(addr + 1));
    bigInt->SetSign(sign);
    return BigIntHelper::RightTruncate(thread, bigInt);
}

JSHandle<BigInt> BigInt::Uint64ToBigInt(JSThread *thread, const uint64_t &number)
{
    JSHandle<BigInt> bigInt = CreateBigInt(thread, 2);  // 2 : one int64_t bits need two uint32_t bits
    const auto *addr = reinterpret_cast<const uint32_t *>(&number);
    BigInt::SetDigit(thread, bigInt, 0, *(addr));
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
    BigInt::SetDigit(thread, bigInt, 1, *(addr + 1));
    return BigIntHelper::RightTruncate(thread, bigInt);
}

void BigInt::BigIntToInt64(JSThread *thread, JSHandle<JSTaggedValue> bigInt, int64_t *cValue, bool *lossless)
{
    ASSERT(cValue != nullptr);
    ASSERT(lossless != nullptr);
    JSHandle<BigInt> bigInt64(thread, JSTaggedValue::ToBigInt64(thread, bigInt));
    RETURN_IF_ABRUPT_COMPLETION(thread);
    if (Equal(thread, bigInt64.GetTaggedValue(), bigInt.GetTaggedValue())) {
        *lossless = true;
    }
    auto *addr = reinterpret_cast<uint32_t *>(cValue);
    auto len = static_cast<int>(bigInt64->GetLength());
    for (int index = len - 1; index >= 0; --index) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
        *(addr + index) = bigInt64->GetDigit(index);
    }
    if (bigInt64->GetSign()) {
        *cValue = ~(static_cast<uint64_t>(static_cast<int64_t>(*cValue - 1)));
    }
}

void BigInt::BigIntToUint64(JSThread *thread, JSHandle<JSTaggedValue> bigInt, uint64_t *cValue, bool *lossless)
{
    ASSERT(cValue != nullptr);
    ASSERT(lossless != nullptr);
    JSHandle<BigInt> bigUint64(thread, JSTaggedValue::ToBigUint64(thread, bigInt));
    RETURN_IF_ABRUPT_COMPLETION(thread);
    if (Equal(thread, bigUint64.GetTaggedValue(), bigInt.GetTaggedValue())) {
        *lossless = true;
    }
    auto *addr = reinterpret_cast<uint32_t *>(cValue);
    auto len = static_cast<int>(bigUint64->GetLength());
    for (int index = len - 1; index >= 0; --index) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
        UnalignedStore(addr + index, bigUint64->GetDigit(index));
    }
}

JSHandle<BigInt> BigInt::CreateBigWords(JSThread *thread, bool sign, uint32_t size, const uint64_t *words)
{
    ASSERT(words != nullptr);
    uint32_t needLen = size * 2;  // 2 : uint64_t size to uint32_t size
    JSHandle<BigInt> bigInt = CreateBigInt(thread, needLen);
    const auto *digits = reinterpret_cast<const uint32_t *>(words);
    for (uint32_t index = 0; index < needLen; ++index) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
        SetDigit(thread, bigInt, index, *(digits + index));
    }
    bigInt->SetSign(sign);
    return BigIntHelper::RightTruncate(thread, bigInt);
}

JSHandle<BigInt> BigInt::Add(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    bool xSignFlag = x->GetSign();
    bool ySignFlag = y->GetSign();
    // x + y == x + y
    // -x + -y == -(x + y)
    if (xSignFlag == ySignFlag) {
        return BigIntAdd(thread, x, y, xSignFlag);
    }
    // x + -y == x - y == -(y - x)
    // -x + y == y - x == -(x - y)
    uint32_t xLength = x->GetLength();
    uint32_t yLength = y->GetLength();
    uint32_t i = xLength - 1;
    auto subSize = static_cast<int>(xLength - yLength);
    if (subSize > 0) {
        return BigIntSub(thread, x, y, xSignFlag);
    }

    if (subSize == 0) {
        while (i > 0 && x->GetDigit(i) == y->GetDigit(i)) {
            i--;
        }
        if ((x->GetDigit(i) > y->GetDigit(i))) {
            return BigIntSub(thread, x, y, xSignFlag);
        }

        return BigIntSub(thread, y, x, ySignFlag);
    }

    return BigIntSub(thread, y, x, ySignFlag);
}
JSHandle<BigInt> BigInt::Subtract(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    bool xSignFlag = x->GetSign();
    bool ySignFlag = y->GetSign();
    if (xSignFlag != ySignFlag) {
        // x - (-y) == x + y
        // (-x) - y == -(x + y)
        return BigIntAdd(thread, x, y, xSignFlag);
    }
    // x - y == -(y - x)
    // (-x) - (-y) == y - x == -(x - y)
    uint32_t xLength = x->GetLength();
    uint32_t yLength = y->GetLength();
    uint32_t i = xLength - 1;
    auto subSize = static_cast<int>(xLength - yLength);
    if (subSize > 0) {
        return BigIntSub(thread, x, y, xSignFlag);
    }

    if (subSize == 0) {
        while (i > 0 && x->GetDigit(i) == y->GetDigit(i)) {
            i--;
        }

        if ((x->GetDigit(i) > y->GetDigit(i))) {
            return BigIntSub(thread, x, y, xSignFlag);
        }

        return BigIntSub(thread, y, x, !ySignFlag);
    }

    return BigIntSub(thread, y, x, !ySignFlag);
}

JSHandle<BigInt> BigInt::BigIntAdd(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y, bool resultSign)
{
    if (x->GetLength() < y->GetLength()) {
        return BigIntAdd(thread, y, x, resultSign);
    }
    JSHandle<BigInt> bigInt = BigInt::CreateBigInt(thread, x->GetLength() + 1);
    uint32_t bigIntCarry = 0;
    uint32_t i = 0;
    while (i < y->GetLength()) {
        uint32_t newBigIntCarry = 0;
        uint32_t addPlus = BigIntHelper::AddHelper(x->GetDigit(i), y->GetDigit(i), newBigIntCarry);
        addPlus = BigIntHelper::AddHelper(addPlus, bigIntCarry, newBigIntCarry);
        SetDigit(thread, bigInt, i, addPlus);
        bigIntCarry = newBigIntCarry;
        i++;
    }
    while (i < x->GetLength()) {
        uint32_t newBigIntCarry = 0;
        uint32_t addPlus = BigIntHelper::AddHelper(x->GetDigit(i), bigIntCarry, newBigIntCarry);
        SetDigit(thread, bigInt, i, addPlus);
        bigIntCarry = newBigIntCarry;
        i++;
    }
    SetDigit(thread, bigInt, i, bigIntCarry);
    bigInt->SetSign(resultSign);
    return BigIntHelper::RightTruncate(thread, bigInt);
}

inline uint32_t BigIntHelper::AddHelper(uint32_t x, uint32_t y, uint32_t &bigIntCarry)
{
    uint32_t addPlus = x + y;
    if (addPlus < x) {
        bigIntCarry += 1;
    }
    return addPlus;
}

JSHandle<BigInt> BigInt::BigIntSub(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y, bool resultSign)
{
    JSHandle<BigInt> bigInt = BigInt::CreateBigInt(thread, x->GetLength());
    uint32_t bigIntCarry = 0;
    uint32_t i = 0;
    while (i < y->GetLength()) {
        uint32_t newBigIntCarry = 0;
        uint32_t minuSub = BigIntHelper::SubHelper(x->GetDigit(i), y->GetDigit(i), newBigIntCarry);
        minuSub = BigIntHelper::SubHelper(minuSub, bigIntCarry, newBigIntCarry);
        SetDigit(thread, bigInt, i, minuSub);
        bigIntCarry = newBigIntCarry;
        i++;
    }
    while (i < x->GetLength()) {
        uint32_t newBigIntCarry = 0;
        uint32_t minuSub = BigIntHelper::SubHelper(x->GetDigit(i), bigIntCarry, newBigIntCarry);
        SetDigit(thread, bigInt, i, minuSub);
        bigIntCarry = newBigIntCarry;
        i++;
    }
    bigInt->SetSign(resultSign);
    return BigIntHelper::RightTruncate(thread, bigInt);
}

JSHandle<BigInt> BigInt::BigIntAddOne(JSThread *thread, JSHandle<BigInt> x)
{
    JSHandle<BigInt> temp = Int32ToBigInt(thread, 1);
    return Add(thread, x, temp);
}

JSHandle<BigInt> BigInt::BigIntSubOne(JSThread *thread, JSHandle<BigInt> x)
{
    JSHandle<BigInt> temp = Int32ToBigInt(thread, 1);
    return Subtract(thread, x, temp);
}

inline uint32_t BigIntHelper::SubHelper(uint32_t x, uint32_t y, uint32_t &bigIntCarry)
{
    uint32_t minuSub = x - y;
    if (minuSub > x) {
        bigIntCarry += 1;
    }
    return minuSub;
}

ComparisonResult BigInt::Compare(JSThread *thread, const JSTaggedValue &x, const JSTaggedValue &y)
{
    if (!LessThan(thread, x, y)) {
        if (!LessThan(thread, y, x)) {
            return ComparisonResult::EQUAL;
        }
        return ComparisonResult::GREAT;
    }
    return ComparisonResult::LESS;
}

bool BigInt::LessThan(JSThread *thread, const JSTaggedValue &x, const JSTaggedValue &y)
{
    [[maybe_unused]] EcmaHandleScope scope(thread);
    JSHandle<BigInt> xHandle(thread, x);
    JSHandle<BigInt> yHandle(thread, y);
    return LessThan(xHandle, yHandle);
}

bool BigInt::LessThan(const JSHandle<BigInt> &x, const JSHandle<BigInt> &y)
{
    bool xSignFlag = x->GetSign();
    bool ySignFlag = y->GetSign();
    auto minSize = static_cast<int>(x->GetLength() - y->GetLength());
    uint32_t i = x->GetLength() - 1;
    if (xSignFlag != ySignFlag) {
        return xSignFlag;
    }
    if (minSize != 0 && xSignFlag) {
        return minSize > 0;
    }
    if (minSize != 0 && !xSignFlag) {
        return minSize <= 0;
    }
    while (i > 0 && x->GetDigit(i) == y->GetDigit(i)) {
        i--;
    }
    if ((x->GetDigit(i) > y->GetDigit(i))) {
        return xSignFlag;
    }
    if ((x->GetDigit(i) < y->GetDigit(i))) {
        return !xSignFlag;
    }
    return false;
}

JSHandle<BigInt> BigInt::SignedRightShift(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    bool xIsNull = (x->GetDigit(0) == 0U);
    bool yIsNull = (y->GetDigit(0) == 0U);
    if (xIsNull || yIsNull) {
        return x;
    }
    if (y->GetSign()) {
        return LeftShiftHelper(thread, x, y);
    }
    return RightShiftHelper(thread, x, y);
}

JSHandle<BigInt> BigInt::RightShiftHelper(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    std::string shiftBinay = BigIntHelper::GetBinary(x);
    std::string revTemp = std::string(shiftBinay.rbegin(), shiftBinay.rend());
    for (uint32_t i = 0; i < y->GetLength(); i++) {
        revTemp = revTemp.erase(0, y->GetDigit(i));
    }
    std::string finalBinay = std::string(revTemp.rbegin(), revTemp.rend());
    if (finalBinay.empty()) {
        finalBinay = "0";
    }
    JSHandle<BigInt> bigInt = BigIntHelper::SetBigInt(thread, finalBinay, BINARY);
    if (x->GetSign()) {
        SetDigit(thread, bigInt, 0, bigInt->GetDigit(0) + 1);
    }
    bigInt->SetSign(x->GetSign());
    return BigIntHelper::RightTruncate(thread, bigInt);
}

JSHandle<BigInt> BigInt::LeftShift(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    if (y->GetSign()) {
        return RightShiftHelper(thread, x, y);
    }
    return LeftShiftHelper(thread, x, y);
}

JSHandle<BigInt> BigInt::LeftShiftHelper(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    std::string shiftBinary = BigIntHelper::GetBinary(x);
    for (size_t i = 0; i < y->GetLength(); i++) {
        shiftBinary = shiftBinary.append(y->GetDigit(i), '0');
    }
    JSHandle<BigInt> bigInt = BigIntHelper::SetBigInt(thread, shiftBinary, BINARY);
    bigInt->SetSign(x->GetSign());
    return BigIntHelper::RightTruncate(thread, bigInt);
}

JSHandle<BigInt> BigInt::UnsignedRightShift(JSThread *thread, [[maybe_unused]] JSHandle<BigInt> x,
                                            [[maybe_unused]] JSHandle<BigInt> y)
{
    [[maybe_unused]] EcmaHandleScope scope(thread);
    JSHandle<BigInt> exception(thread, JSTaggedValue::Exception());
    THROW_TYPE_ERROR_AND_RETURN(thread, "BigInt have no unsigned right shift, use >> instead", exception);
}

JSHandle<BigInt> BigInt::Copy(JSThread *thread, JSHandle<BigInt> x)
{
    uint32_t len = x->GetLength();
    JSHandle<BigInt> temp = CreateBigInt(thread, len);
    for (uint32_t i = 0; i < len; i++) {
        SetDigit(thread, temp, i, x->GetDigit(i));
    }
    temp->SetSign(x->GetSign());
    return temp;
}

JSHandle<BigInt> BigInt::UnaryMinus(JSThread *thread, JSHandle<BigInt> x)
{
    if (x->IsZero()) {
        return x;
    }
    JSHandle<BigInt> y = Copy(thread, x);
    y->SetSign(!y->GetSign());
    return y;
}

// 6.1.6.2.2   BigInt::bitwiseNOT ( x )
JSHandle<BigInt> BigInt::BitwiseNOT(JSThread *thread, JSHandle<BigInt> x)
{
    // ~(-x) == ~(~(x-1)) == x-1
    // ~x == -x-1 == -(x+1)
    JSHandle<BigInt> result = BigIntAddOne(thread, x);
    if (x->GetSign()) {
        result->SetSign(false);
    } else {
        result->SetSign(true);
    }
    return result;
}

JSHandle<BigInt> BigInt::Exponentiate(JSThread *thread, JSHandle<BigInt> base, JSHandle<BigInt> exponent)
{
    if (exponent->GetSign()) {
        JSHandle<BigInt> bigInt(thread, JSTaggedValue::Exception());
        THROW_RANGE_ERROR_AND_RETURN(thread, "Exponent must be positive", bigInt);
    }
    ASSERT(exponent->GetLength() == 1);
    if (exponent->IsZero()) {
        return BigIntHelper::SetBigInt(thread, "1");
    }

    if (base->IsZero()) {
        return BigIntHelper::SetBigInt(thread, "0");
    }
    uint32_t eValue = exponent->GetDigit(0);
    if (eValue == 1) {
        return base;
    }
    uint32_t j = exponent->GetDigit(0) - 1;
    std::string a = BigIntHelper::GetBinary(base);
    a = BigIntHelper::Conversion(a, DECIMAL, BINARY);
    std::string b = a;
    for (uint32_t i = 0; i < j; ++i) {
        b = BigIntHelper::MultiplyImpl(b, a);
    }
    if ((exponent->GetDigit(0) & 1U) != 0U) {
        if (base->GetSign()) {
            b = "-" + b;
        }
    }
    return BigIntHelper::SetBigInt(thread, b, DECIMAL);
}

std::string BigIntHelper::MultiplyImpl(std::string &a, std::string &b)
{
    auto size1 = static_cast<int>(a.size());
    auto size2 = static_cast<int>(b.size());
    std::string str(size1 + size2, '0');
    for (int i = size2 - 1; i >= 0; --i) {
        int mulflag = 0;
        int addflag = 0;
        for (int j = size1 - 1; j >= 0; --j) {
            int temp1 = (b[i] - '0') * (a[j] - '0') + mulflag;
            // NOLINTNEXTLINE(readability-magic-numbers)
            mulflag = temp1 / 10;  // 10:help to Remove single digits
            // NOLINTNEXTLINE(readability-magic-numbers)
            temp1 = temp1 % 10;  // 10:help to Take single digit
            int temp2 = str[i + j + 1] - '0' + temp1 + addflag;
            // NOLINTNEXTLINE(readability-magic-numbers)
            str[i + j + 1] = static_cast<int8_t>(temp2 % 10 + '0');  // 2 and 10 and 48 is number
                                                                     // NOLINTNEXTLINE(readability-magic-numbers)
            addflag = temp2 / 10;
        }
        str[i] += static_cast<int8_t>(mulflag + addflag);
    }
    if (str[0] == '0') {
        str = str.substr(1, str.size());
    }
    return str;
}

JSHandle<BigInt> BigInt::Multiply(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    if (x->IsZero()) {
        return x;
    }
    if (y->IsZero()) {
        return y;
    }
    std::string left = BigIntHelper::GetBinary(x);
    std::string right = BigIntHelper::GetBinary(y);
    left = BigIntHelper::Conversion(left, DECIMAL, BINARY);
    right = BigIntHelper::Conversion(right, DECIMAL, BINARY);
    std::string ab = BigIntHelper::MultiplyImpl(left, right);
    if (x->GetSign() != y->GetSign()) {
        ab = "-" + ab;
    }
    return BigIntHelper::SetBigInt(thread, ab, DECIMAL);
}

std::string BigIntHelper::DeZero(std::string &a)
{
    size_t i;
    for (i = 0; i < a.length(); i++) {
        // NOLINTNEXTLINE(readability-magic-numbers)
        if (a.at(i) > 48) {  // 48 is ascill of '0'
            break;
        }
    }
    if (i == a.length()) {
        return "0";
    }
    a.erase(0, i);
    return a;
}

Comparestr BigInt::ComString(std::string &a, std::string &b)
{
    if (a.length() > b.length()) {
        return Comparestr::GREATER;
    }
    if (a.length() < b.length()) {
        return Comparestr::LESS;
    }
    for (size_t i = 0; i < a.length(); i++) {
        if (a.at(i) > b.at(i)) {
            return Comparestr::GREATER;
        }
        if (a.at(i) < b.at(i)) {
            return Comparestr::LESS;
        }
    }
    return Comparestr::EQUAL;
}

std::string BigIntHelper::DevStr(std::string &strValue)
{
    size_t i = 0;
    for (i = 0; i < strValue.length(); i++) {
        // NOLINTNEXTLINE(readability-magic-numbers)
        if (strValue.at(i) >= 48 && strValue.at(i) <= 57) {  // 48 and 57 is '0' and '9'
            // NOLINTNEXTLINE(readability-magic-numbers)
            strValue.at(i) -= 48;  // 48:'0'
        }
        // NOLINTNEXTLINE(readability-magic-numbers)
        if (strValue.at(i) >= 97 && strValue.at(i) <= 122) {  // 97 and 122 is 'a' and 'z'
            // NOLINTNEXTLINE(readability-magic-numbers)
            strValue.at(i) -= 87;  // 87 control result is greater than 10
        }
    }
    return strValue;
}

std::string BigIntHelper::Minus(std::string &a, std::string &b)
{
    a = DeZero(a);
    b = DeZero(b);
    size_t i = 0;
    int j = 0;
    std::string res = "0";
    std::string result1;
    std::string result2;
    std::string dsymbol = "-";
    if (BigInt::ComString(a, b) == Comparestr::EQUAL) {
        return res;
    }
    if (BigInt::ComString(a, b) == Comparestr::GREATER) {
        result1 = a;
        result2 = b;
    }
    if (BigInt::ComString(a, b) == Comparestr::LESS) {
        result1 = b;
        result2 = a;
        j = -1;
    }
    reverse(result1.begin(), result1.end());
    reverse(result2.begin(), result2.end());
    result1 = DevStr(result1);
    result2 = DevStr(result2);
    for (i = 0; i < result2.length(); i++) {
        result1.at(i) = result1.at(i) - result2.at(i);
    }
    for (i = 0; i < result1.length() - 1; i++) {
        if (((int8_t)result1.at(i)) < 0) {
            result1.at(i) += BigInt::DECIMAL;
            result1.at(i + 1)--;
        }
    }
    for (i = result1.length() - 1; i != 0; i--) {
        if (result1.at(i) > 0) {
            break;
        }
    }
    result1.erase(i + 1, result1.length());
    for (i = 0; i < result1.length(); i++) {
        // NOLINTNEXTLINE(readability-magic-numbers)
        if (result1.at(i) >= 10) {  // 10:Hexadecimal a
            // NOLINTNEXTLINE(readability-magic-numbers)
            result1.at(i) += 87;  // 87:control result is greater than 97
        }
        // NOLINTNEXTLINE(readability-magic-numbers)
        if (result1.at(i) < 10) {  // 10: 10:Hexadecimal a
            // NOLINTNEXTLINE(readability-magic-numbers)
            result1.at(i) += 48;  // 48:'0'
        }
    }
    reverse(result1.begin(), result1.end());
    if (j == -1) {
        result1.insert(0, dsymbol);
    }
    return result1;
}

std::string BigIntHelper::Divide(std::string &a, std::string &b)
{
    size_t i = 0;
    size_t j = 0;
    std::string result1;
    std::string result2;
    std::string dsy;
    std::string quotient;
    if (BigInt::ComString(a, b) == Comparestr::EQUAL) {
        return "1";
    }
    if (BigInt::ComString(a, b) == Comparestr::LESS) {
        return "0";
    }
    result1 = DeZero(a);
    result2 = DeZero(b);
    dsy = "";
    quotient = "";
    for (i = 0; i < result1.length(); i++) {
        j = 0;
        dsy += result1.at(i);
        dsy = DeZero(dsy);
        while (BigInt::ComString(dsy, b) == Comparestr::EQUAL || BigInt::ComString(dsy, b) == Comparestr::GREATER) {
            dsy = Minus(dsy, b);
            dsy = DeZero(dsy);
            j++;
        }
        quotient += "0";
        quotient.at(i) = static_cast<int8_t>(j);
    }
    for (i = 0; i < quotient.length(); i++) {
        // NOLINTNEXTLINE(readability-magic-numbers)
        if (quotient.at(i) >= 10) {  // 10 is number
            // NOLINTNEXTLINE(readability-magic-numbers)
            quotient.at(i) += 87;  // 87 is number
        }
        // NOLINTNEXTLINE(readability-magic-numbers)
        if (quotient.at(i) < 10) {  // 10 is number
            // NOLINTNEXTLINE(readability-magic-numbers)
            quotient.at(i) += 48;  // 48 is number
        }
    }
    quotient = DeZero(quotient);
    return quotient;
}

JSHandle<BigInt> BigIntHelper::DivideImpl(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    std::string a = Conversion(GetBinary(x), BigInt::DECIMAL, BigInt::BINARY);
    std::string b = Conversion(GetBinary(y), BigInt::DECIMAL, BigInt::BINARY);
    std::string ab = Divide(a, b);
    if (ab == "0") {
        ab = "0";
    } else if (x->GetSign() != y->GetSign()) {
        ab = "-" + ab;
    }
    return SetBigInt(thread, ab, BigInt::DECIMAL);
}

JSHandle<BigInt> BigInt::Divide(JSThread *thread, JSHandle<BigInt> x, JSHandle<BigInt> y)
{
    if (y->IsZero()) {
        JSHandle<BigInt> bigInt(thread, JSTaggedValue::Exception());
        THROW_RANGE_ERROR_AND_RETURN(thread, "Division by zero", bigInt);
    }
    return BigIntHelper::DivideImpl(thread, x, y);
}

JSHandle<BigInt> BigInt::Remainder(JSThread *thread, JSHandle<BigInt> n, JSHandle<BigInt> d)
{
    if (d->IsZero()) {
        JSHandle<BigInt> bigInt(thread, JSTaggedValue::Exception());
        THROW_RANGE_ERROR_AND_RETURN(thread, "Division by zero", bigInt);
    }
    if (n->IsZero()) {
        return n;
    }
    JSHandle<BigInt> q = Divide(thread, n, d);
    JSHandle<BigInt> p = Multiply(thread, q, d);
    return Subtract(thread, n, p);
}

JSHandle<BigInt> BigInt::FloorMod(JSThread *thread, JSHandle<BigInt> leftVal, JSHandle<BigInt> rightVal)
{
    if (leftVal->GetSign()) {
        JSHandle<BigInt> quotientVal = Divide(thread, leftVal, rightVal);
        if (quotientVal->IsZero()) {
            return Add(thread, leftVal, rightVal);
        }
        JSHandle<BigInt> num = Multiply(thread, quotientVal, rightVal);
        if (Equal(num, leftVal)) {
            return Int32ToBigInt(thread, 0);
        }
        return Subtract(thread, leftVal, Subtract(thread, num, rightVal));
    }
    return Remainder(thread, leftVal, rightVal);
}

JSTaggedValue BigInt::AsUintN(JSThread *thread, JSTaggedNumber &bits, JSHandle<BigInt> bigInt)
{
    uint32_t bit = bits.ToUint32();
    if (bit == 0) {
        return Int32ToBigInt(thread, 0).GetTaggedValue();
    }
    if (bigInt->IsZero()) {
        return bigInt.GetTaggedValue();
    }
    JSHandle<BigInt> exponent = Int32ToBigInt(thread, bit);
    JSHandle<BigInt> base = Int32ToBigInt(thread, 2);  // 2 : base value
    JSHandle<BigInt> tValue = Exponentiate(thread, base, exponent);
    return FloorMod(thread, bigInt, tValue).GetTaggedValue();
}

JSTaggedValue BigInt::AsintN(JSThread *thread, JSTaggedNumber &bits, JSHandle<BigInt> bigInt)
{
    uint32_t bit = bits.ToUint32();
    if (bit == 0) {
        return Int32ToBigInt(thread, 0).GetTaggedValue();
    }
    if (bigInt->IsZero()) {
        return bigInt.GetTaggedValue();
    }
    JSHandle<BigInt> exp = Int32ToBigInt(thread, bit);
    JSHandle<BigInt> exponent = Int32ToBigInt(thread, static_cast<int32_t>(bit - 1));
    JSHandle<BigInt> base = Int32ToBigInt(thread, 2);  // 2 : base value
    JSHandle<BigInt> tValue = Exponentiate(thread, base, exp);
    JSHandle<BigInt> modValue = FloorMod(thread, bigInt, tValue);
    JSHandle<BigInt> resValue = Exponentiate(thread, base, exponent);
    // If mod ≥ 2bits - 1, return ℤ(mod - 2bits); otherwise, return (mod).
    if (LessThan(resValue, modValue) || Equal(resValue, modValue)) {
        return Subtract(thread, modValue, tValue).GetTaggedValue();
    }
    return modValue.GetTaggedValue();
}

static JSTaggedNumber CalculateNumber(const uint64_t &sign, const uint64_t &mantissa, uint64_t &exponent)
{
    exponent = (exponent + base::DOUBLE_EXPONENT_BIAS) << base::DOUBLE_SIGNIFICAND_SIZE;
    uint64_t doubleBit = sign | exponent | mantissa;
    double res = 0;
    if (memcpy_s(&res, sizeof(res), &doubleBit, sizeof(doubleBit)) != EOK) {
        LOG_ECMA(FATAL) << "memcpy_s failed";
        UNREACHABLE();
    }
    return JSTaggedNumber(res);
}

static JSTaggedNumber Rounding(const uint64_t &sign, uint64_t &mantissa, uint64_t &exponent, bool needRound)
{
    if (needRound || (mantissa & 1U) == 1U) {
        ++mantissa;
        if ((mantissa >> base::DOUBLE_SIGNIFICAND_SIZE) != 0) {
            mantissa = 0;
            exponent++;
            if (exponent > base::DOUBLE_EXPONENT_BIAS) {
                return JSTaggedNumber(sign != 0U ? -base::POSITIVE_INFINITY : base::POSITIVE_INFINITY);
            }
        }
    }
    return CalculateNumber(sign, mantissa, exponent);
}

JSTaggedNumber BigInt::BigIntToNumber(JSHandle<BigInt> bigInt)
{
    if (bigInt->IsZero()) {
        return JSTaggedNumber(0);
    }
    uint32_t bigIntLen = bigInt->GetLength();
    uint32_t bigIntHead = bigInt->GetDigit(bigIntLen - 1);
    uint32_t bits = BigInt::DATEBITS;
    uint32_t preZero = 0;
    while (bits-- != 0U) {
        if (((bigIntHead >> bits) | 0U) != 0) {
            break;
        }
        preZero++;
    }
    uint32_t bigIntBitLen = bigIntLen * BigInt::DATEBITS - preZero;
    // if Significant bits greater than 1024 then double is infinity
    bool bigIntSign = bigInt->GetSign();
    if (bigIntBitLen > (base::DOUBLE_EXPONENT_BIAS + 1)) {
        return JSTaggedNumber(bigIntSign ? -base::POSITIVE_INFINITY : base::POSITIVE_INFINITY);
    }
    // NOLINTNEXTLINE(readability-magic-numbers)
    uint64_t sign = bigIntSign ? 1ULL << 63ULL : 0;  // 63 : Set the sign bit of sign to 1
    uint32_t needMoveBit = preZero + BigInt::DATEBITS + 1;
    // Align to the most significant bit, then right shift 12 bits so that the head of the mantissa is in place
    // NOLINTNEXTLINE(readability-magic-numbers)
    auto mantissa = (static_cast<uint64_t>(bigIntHead) << needMoveBit) >> 12ULL;  // 12 mantissa just has 52 bits
    auto remainMantissaBits = static_cast<int32_t>(needMoveBit - 12);             // NOLINT(readability-magic-numbers)
    auto exponent = static_cast<uint64_t>(bigIntBitLen - 1);
    auto index = static_cast<int>(bigIntLen - 1);
    uint32_t digit = 0;
    if (index > 0) {
        digit = bigInt->GetDigit(--index);
    } else {
        return CalculateNumber(sign, mantissa, exponent);
    }
    // pad unset mantissa
    if (static_cast<uint32_t>(remainMantissaBits) >= BigInt::DATEBITS) {
        mantissa |= (static_cast<uint64_t>(digit) << (remainMantissaBits - BigInt::DATEBITS));
        remainMantissaBits -= BigInt::DATEBITS;
        index--;
    }
    if (remainMantissaBits > 0 && index >= 0) {
        digit = bigInt->GetDigit(index);
        mantissa |= (static_cast<uint64_t>(digit) >> (BigInt::DATEBITS - remainMantissaBits));
        remainMantissaBits -= BigInt::DATEBITS;
    }
    // After the mantissa is filled, if the bits of BigInt have not been used up, consider the rounding problem
    // The remaining bits of the current digit
    int remainDigitBits = 1;
    if (remainMantissaBits < 0) {
        remainDigitBits = -remainMantissaBits;
    }
    if (remainMantissaBits == 0) {
        if (index == 0) {
            return CalculateNumber(sign, mantissa, exponent);
        }
        digit = bigInt->GetDigit(index--);
        remainDigitBits = BigInt::DATEBITS;
    }
    uint32_t temp = 1ULL << (remainDigitBits - 1);  // NOLINT(hicpp-signed-bitwise)
    if ((digit & temp) == 0U) {
        return CalculateNumber(sign, mantissa, exponent);
    }
    if ((digit & (temp - 1)) != 0) {
        return Rounding(sign, mantissa, exponent, true);
    }
    while (index > 0) {
        if (bigInt->GetDigit(index--) != 0) {
            return Rounding(sign, mantissa, exponent, true);
        }
    }
    return Rounding(sign, mantissa, exponent, false);
}

static int CompareToBitsLen(JSHandle<BigInt> bigInt, uint32_t numBitLen, uint32_t &preZero)
{
    uint32_t bigIntLen = bigInt->GetLength();
    uint32_t bigIntHead = bigInt->GetDigit(bigIntLen - 1);
    uint32_t bits = BigInt::DATEBITS;
    while (bits != 0U) {
        bits--;
        if (((bigIntHead >> bits) | 0U) != 0) {
            break;
        }
        preZero++;
    }

    uint32_t bigIntBitLen = bigIntLen * BigInt::DATEBITS - preZero;
    bool bigIntSign = bigInt->GetSign();
    if (bigIntBitLen > numBitLen) {
        return bigIntSign ? 0 : 1;
    }

    if (bigIntBitLen < numBitLen) {
        return bigIntSign ? 1 : 0;
    }
    return -1;
}

ComparisonResult BigInt::CompareWithNumber(JSHandle<BigInt> bigInt, JSHandle<JSTaggedValue> number)
{
    double num = number->GetNumber();
    bool numberSign = num < 0;
    if (std::isnan(num)) {
        return ComparisonResult::UNDEFINED;
    }
    if (!std::isfinite(num)) {
        return (!numberSign ? ComparisonResult::LESS : ComparisonResult::GREAT);
    }
    // Bit operations must be of integer type
    uint64_t bits = 0;
    if (memcpy_s(&bits, sizeof(bits), &num, sizeof(num)) != EOK) {
        LOG_ECMA(FATAL) << "memcpy_s failed";
        UNREACHABLE();
    }
    uint32_t exponential = (bits >> base::DOUBLE_SIGNIFICAND_SIZE) & 0x7FFU;  // NOLINT(readability-magic-numbers)

    // Take out bits 62-52 (11 bits in total) and subtract 1023
    auto integerDigits = static_cast<int32_t>(exponential - base::DOUBLE_EXPONENT_BIAS);
    uint64_t mantissa = (bits & base::DOUBLE_SIGNIFICAND_MASK) | base::DOUBLE_HIDDEN_BIT;
    bool bigIntSign = bigInt->GetSign();
    // Handling the opposite sign
    if (!numberSign && bigIntSign) {
        return ComparisonResult::LESS;
    }
    if (numberSign && !bigIntSign) {
        return ComparisonResult::GREAT;
    }
    if (bigInt->IsZero() && num == 0.0) {
        return ComparisonResult::EQUAL;
    }
    if (bigInt->IsZero() && num > 0) {
        return ComparisonResult::LESS;
    }

    if (integerDigits < 0) {
        return bigIntSign ? ComparisonResult::LESS : ComparisonResult::GREAT;
    }

    // Compare the significant bits of BigInt with the significant integer bits of double
    uint32_t preZero = 0;
    int res = CompareToBitsLen(bigInt, integerDigits + 1, preZero);
    if (res == 0) {
        return ComparisonResult::LESS;
    }
    if (res == 1) {
        return ComparisonResult::GREAT;
    }
    int mantissaSize = base::DOUBLE_SIGNIFICAND_SIZE;  // mantissaSize
    uint32_t bigIntLen = bigInt->GetLength();
    uint32_t leftover = 0;
    bool isFirstInto = true;
    for (int index = static_cast<int>(bigIntLen - 1); index >= 0; --index) {
        uint32_t doubleNum = 0;
        uint32_t bigIntNum = bigInt->GetDigit(index);
        if (isFirstInto) {
            isFirstInto = false;
            leftover = mantissaSize - BigInt::DATEBITS + preZero + 1;
            doubleNum = static_cast<uint32_t>(mantissa >> leftover);
            mantissa = mantissa << (64U - leftover);  // NOLINT(readability-magic-numbers) 64 : double bits
            if (bigIntNum > doubleNum) {
                return bigIntSign ? ComparisonResult::LESS : ComparisonResult::GREAT;
            }
            if (bigIntNum < doubleNum) {
                return bigIntSign ? ComparisonResult::GREAT : ComparisonResult::LESS;
            }
        } else {
            leftover -= BigInt::DATEBITS;
            doubleNum = static_cast<uint32_t>(mantissa >> BigInt::DATEBITS);
            mantissa = mantissa << BigInt::DATEBITS;
            if (bigIntNum > doubleNum) {
                return bigIntSign ? ComparisonResult::LESS : ComparisonResult::GREAT;
            }
            if (bigIntNum < doubleNum) {
                return bigIntSign ? ComparisonResult::GREAT : ComparisonResult::LESS;
            }
            leftover -= BigInt::DATEBITS;
        }
    }

    if (mantissa != 0) {
        ASSERT(leftover > 0);
        return bigIntSign ? ComparisonResult::GREAT : ComparisonResult::LESS;
    }
    return ComparisonResult::EQUAL;
}
}  // namespace ark::ecmascript
